<?php
/**
 * html_helper.class.php
 *
 * HTML tags renderer
 *
 * @version:	0.1
 * @package:	core
 * @author:		alex_ez
 * @created:	 2010
 *
 * @copyright:	(c) 2008 flamebox.ru. All rights reserved.
 */

load_helper( 'url' );

// basic ~~~

function html_escape( $s )
{
	return htmlentities( $s, ENT_QUOTES, 'UTF-8' );
}

function html_attributes( $atts )
{
	$atts = (array)($atts);
	$out = '';
	
	foreach( $atts as $a => $v )
	if( $a != 'text' && $a != 'html' && !is_null( $v ) )
		$out .= sprintf( '%s="%s" ', $a, html_escape( $v ) );
	
	return $out;
}

function html_open_element( $el, $atts = array( ) )
{
	$el = (string)($el);
	
	$out = $el.' ';
	$out .= html_attributes( $atts );
	
	return '<'.$out.'>';
}

function html_close_element( $el )
{
	$el = (string)($el);
	return "</$el>";
}

function html_element( $el, $html, $atts = array( ) )
{
	if (is_string($html))		$atts['html'] = $html;
	elseif (is_array($html))	$atts = $html;

	$out = html_open_element( $el, $atts );
	$out .= isset( $atts['text'] ) ? html_escape( $atts['text'] ) : '';
	$out .= isset( $atts['html'] ) ? $atts['html'] : '';
	$out .= html_close_element( $el );
	return $out;
}

function html_empty_element( $el, $atts = array( ) )
{
	$el = (string)($el);
	$atts = html_attributes( $atts );
	return sprintf( "<%s />", trim( $el.' '.$atts ) );
}

// ~~~ basic




function html_heading( $data = '', $h = 1, $is_text = false )
{
	$h = min(max($h,1),6);
	return html_element( 'h'.$h, array( $is_text ? 'text' : 'html' => $data ) );
}

function html_br( $class = '', $atts = array( ) )
{
	$atts = (array)$atts;
	if( !empty( $class ) )
		$atts['class'] = is_array( $class ) ? join( ' ', $class ) : (string)($class);
	return html_empty_element( 'br', $atts );
}

function html_img( $atts = array( ) )
{
	if( !is_array( $atts ) )
	{
		$atts = (string)($atts);
		if( empty( $atts ) ) return;
		
		$atts = array( 'src' => $atts, 'alt' => $atts );
	}
	
	if( !isset( $atts['src'] ) )
		$atts['src'] = $src;
	if( !isset( $atts['alt'] ) )
		$atts['alt'] = '';
	
	$atts['src'] = ( strpos($atts['src'], '://') === false )
		? site_url( $atts['src'] ) : $atts['src'] ;
	
	return html_empty_element( 'img', $atts );
}

function html_a( $href, $text = '', $class = '', $title = '', $atts = array( ) )
{
	$args = func_get_args( );
	$args_index = array( 'href', 'text', 'class', 'title' );
	
	$atts = end( $args );
	if( is_array( $atts ) ) $args[key($args)] = null; else $atts = array( );
	
	foreach( $args_index as $k => $v ) if( !isset( $args[$k] ) || is_array( $args[$k] ) ) $args[$k] = null;
	$args = array_combine( $args_index, array_splice( $args, 0, 4 ) );

	foreach( $args as $k => $v )
		$atts[$k] = !empty( $v ) ? $v : ( isset( $atts[$k] ) ? $atts[$k] : null );
	
	if( isset( $atts['class'] ) && !empty( $atts['class'] ) )
		$atts['class'] = is_array( $atts['class'] ) ? join( ' ', $atts['class'] ) : (string)($atts['class']);
	
	if( isset( $atts['href'] ) )
		$atts['href'] = ( strpos($atts['href'], '://') === false ) ? site_url( $atts['href'] ) : $atts['href'] ;

	return html_element( 'a', $atts );
}

function html_span( $html, $atts = array( ) )
{
	if (empty($atts) || !is_array($atts))
		$atts = array();

	if (is_array($html))
		$atts = $html;
	else
		$atts['html'] = $html;

	return html_element( 'span', $atts );
}

function html_div( $html, $atts = array( ) ) {
	if (empty($atts) || !is_array($atts))
		$atts = array();

	if (is_array($html))
		$atts = $html;
	else
		$atts['html'] = $html;

	return html_element( 'div', $atts );
}

function html_p( $html, $atts = array( ) ) {
	if (empty($atts) || !is_array($atts))
		$atts = array();

	if (is_array($html))
		$atts = $html;
	else
		$atts['html'] = $html;

	return html_element( 'p', $atts );
}

function html_doctype( $type = 'html5' )
{
	$doctypes = array( );
	$doctypes['xhtml11'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">';
	$doctypes['xhtml1-strict'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
	$doctypes['xhtml1-trans'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
	$doctypes['xhtml1-frame'] = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">';
	$doctypes['html4-strict'] = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">';
	$doctypes['html4-trans'] = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
	$doctypes['html4-frame'] = '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">';
	$doctypes['html5'] = '<!DOCTYPE html>';
	
	if( !isset( $doctypes[$type] ) )
		$type = 'html5';
	
	return $doctypes[$type];
}

function html_abbr( $text, $title_or_length = null )
{
	$test = is_int($title_or_length) && $title_or_length;
	$length = $test ? $title_or_length : 0;
	$title = $test ? $text : ( empty($title_or_length) ? $text : $title_or_length );

	$text = $title == $text ? texo::shorter( $text, $length ) : $text;
	return html_element( 'abbr', compact( 'text', 'title' ) );
}

function html_cycle( $cycle, $name = null )
{
	$name = !is_null( $name ) ? $name : md5( $cycle );
	$items = explode( ',', $cycle );
	
	static $cycles;
	if( !isset( $cycles[$name] ) ) $cycles[$name] = 0;
	
	$current = $cycles[$name]%count($items);
	$r = $items[$current];
	
	$cycles[$name]++;
	return $r;
}















/**
 * html_table helper
 *
 * make an list html table from an array of data
 *
 * @examples
 * html_table( $data );
 * html_table( $data, array( 'cols' => 4 )); // 4 columns with key names
 * html_table( $data, array( 'cols' => "first,second,third", 'spacer' => '&nbsp;&mdash;&nbsp;' ));
 *
 * @param array data source to loop through
 * @param array parameters:
 *  cols:	number of columns or comma separated list with column names or array of column names
 *  spacer:	string which will print when no data in cell
 *  headtype:	none (no headers), top, bottom or both (top and bottom)
 * @return string
 */
function html_table( $data, $params = array( ) )
{
	if( empty($data) || !is_array($data) ) return '<pre>html_table: no data to show</pre>';
	$params['_header'] = html_table__parse_cols( isset( $params['cols'] ) ? $params['cols'] : join('|',array_keys(reset($data))) );
	
	$params['spacer'] = isset($params['spacer'])?$params['spacer']:"&mdash;";
	$params['headtype'] = isset($params['headtype']) ? (min(max(intval($params['headtype']),0),3)) : 1; //0-none,1-top,2-bottom,3-both
	
	$_columns = 0;
	if( !is_null( $params['_header'] ) )
		foreach( $params['_header'][0] as $col ) $_columns += $col['columns'];
	elseif( !empty($data) )
	{
//		if( $params['headtype'] )
//			$params['_header'] = array_keys(reset($params['from']));
		$_columns = sizeof(reset($data));
	}
	$params['_columns'] = $_columns;
	
	$params['from'] = $data;
	$_r = view::instance()->display( "bits/tablelist.sma", $params );
	
	return $_r;
}

function html_table__parse_cols( $cols )
{
	if( !is_string( $cols ) && !is_array( $cols ) ) return null;
	if( is_string( $cols ) )
		$cols = html_table__fetch_cols( $cols );
	elseif(is_array($cols))
		foreach(array_keys($cols) as $col_id)
			if(!is_array($cols[$col_id]))
				$cols[$col_id] = array('name'=>$cols[$col_id]);
	
	html_table__flat_cols( $cols );
	html_table__calculate_depth( $cols );
	html_table__calculate_span( $cols );
	
	/*echo "<pre>";
	foreach( range( 0,5 ) as $root_id )
	foreach( $cols as $k => $col )
	if( $col['root']['id'] == $root_id )
		echo ($col['id']==$root_id ? "\n" : '') . $col['id']."\t".$col['columns']."cols \twdub: ".$col['weight'].' '.$col['depth'].' '.$col['upperrows'].' !'.$col['root']['branch_depth']."\t".$col['rows']."rows\t".
		(isset($col['parent']['parent']['parent']['parent']['parent'])?$col['parent']['parent']['parent']['parent']['parent']['name'].' > ':'').
		(isset($col['parent']['parent']['parent']['parent'])?$col['parent']['parent']['parent']['parent']['name'].' > ':'').
		(isset($col['parent']['parent']['parent'])?$col['parent']['parent']['parent']['name'].' > ':'').
		(isset($col['parent']['parent'])?$col['parent']['parent']['name'].' > ':'').
		(isset($col['parent'])		?$col['parent']['name']		.' > ':'').
		$col['name']."\n";
	echo "</pre>";/**/
	
	//ini_set( 'xdebug.var_display_max_depth', '10' );var_dump( $cols );die;
	return html_table__build_header( $cols );
}

function html_table__fetch_cols( $cols, $depth = 0 )
{
	$_r = array( );
	
	while( strlen( $cols ) )
	{
		$stop = false;
	        $_col = array( );
		$_col['name'] = "";
		$_col['depth'] = $depth;
	
// parser block
	for( $l=mb_strlen($cols),$i=0; !$stop&&$i<$l; $i++)
	{
		$ch = mb_substr( $cols, $i, 1 );
		switch( $ch ) {
		case '|': $stop = true; break;
		case ']': die( "table columns parse error: unclosed parenthesis ], cols: \"".$cols."\", pos: ".$i ); break;
		case '[':
			$_sub = mb_substr( $cols, $i+1 );
			$pj = 1;
			$chj = "\0";
			// parenthesis validation.
			for( $lj = mb_strlen($_sub),$j=0,$pj=1; $pj&&$j<=$lj; $chj=mb_substr($_sub,++$j,1) )
				$pj += ($chj===']') ? (-1) : ( ($chj==='[') ? 1 : 0 );
			
			// parenthesis parse error. errors with unclosed parenthesis. like "[asdasd" or "...[asdasd[a|z]|qwe[v|f]"
			if( $pj ) die( "table columns parse error: unclosed parenthesis [, cols: \"".$_sub."\", pos: ".$i );
			
			// erase end of substring
			//var_dump( array( 'sub' => $_sub, 'sub?' => mb_substr( $_sub, 0, $j-1 ), 'pj' => $pj, 'j' => $j, 'len' => $lj ) );
			$_sub = mb_substr( $_sub, 0, $j-1 );
			$lj = mb_strlen( $_sub );
			
			$_col['span'] = html_table__fetch_cols( $_sub, $depth+1 );
			$i+=$lj+1;
			break;
		default: $_col['name'] .= $ch; break;
		}
	}
// end of parser
	
		if( mb_strpos( $_col['name'], ':' ) )
		{
			$options = explode( ':', $_col['name'] );
			//list( $_col['name'], $_col['width'] ) = $options;
			$_col['name'] = array_shift( $options );
			$_col['width'] = array_shift( $options );
			
			foreach( array( ) as $flag ) // flags (?)
			{
				$flagKey = array_search( $flag, $options );
				unset( $options[$flagKey] );
				$_col[ $flag ] = !$flagKey;
			}
			
			parse_str( join( '&', explode( ':', str_replace( '&', "\1", join( ':', $options ) ) ) ), $options );
			//bc::log( $options );
			foreach( array( 'class', 'href', 'title', 'tag' ) as $property )
			if( isset( $options[$property] ) )
				$_col[$property] = str_replace( "\1", '&', $options[$property] );
		}
		
		$_r[] = $_col;
		$cols = mb_substr( $cols, $i );
	}
	
	return $_r;
}

function html_table__calculate_span( &$cols )
{
	$_fc = (reset($cols)); // first column
	for( $depth=$_fc['max_depth']; $depth>=0; $depth-- )
	foreach( array_keys( $cols ) as $k )
	if( $cols[$k]['depth'] == $depth )
	{
		$_col =& $cols[$k]; // current column
		$_col['columns'] = 1; // spanned columns count
		
		// if column have spanned - !
		
		if( isset($_col['span']) && is_array($_col['span']) && !empty($_col['span']) )
		{
			$_col['columns'] = 0;
			//html_table__calculate_span( $_col['span'] );
			foreach( $_col['span'] as $_subcol ) // spanned columns array
				$_col['columns'] += $_subcol['columns'];
		}
	}
	
	for( $depth=0,$l=$_fc['max_depth']; $depth<=$l; $depth++ )
	foreach( array_keys( $cols ) as $k )
	if( $cols[$k]['depth'] == $depth )
	{
		$parentUpperRows = isset( $cols[$k]['parent']['upperrows'] ) ? $cols[$k]['parent']['upperrows'] : 0;
		$_rx = floor( (($l+1-$parentUpperRows)/$cols[$k]['weight']) );
		$cols[$k]['rows'] = $_rx;
		$cols[$k]['upperrows'] = $parentUpperRows+$_rx;
	}
}

function html_table__calculate_depth( &$cols, $depth = 0 )
{
	$max_depth = 0;
	foreach( array_keys($cols) as $k ) {
		$q =& $cols[$k];
		$w = 0;
		$d = 0;
		while( isset( $q['parent'] ) && $q ) {
			$d++;$w--;$q=&$q['parent'];
		}
		$max_depth = max( $d, $max_depth );
		$cols[$k]['max_depth'] =& $max_depth;
		$cols[$k]['depth'] = $d;
		$cols[$k]['weight'] = $w;
	}
	
	// branch depths calculation
	$branch_depths = array();
	foreach( array_keys($cols) as $k )
		$branch_depths[ $cols[$k]['root']['id'] ] = max( $cols[$k]['depth'], @$branch_depths[ $cols[$k]['root']['id'] ] );
	
	foreach( array_keys($cols) as $k )
	if( !$cols[$k]['depth'] )
		$cols[$k]['branch_depth'] = $branch_depths[ $k ] + 1;
	
	// weight calculating
	foreach( array_keys($cols) as $k )
		$cols[$k]['weight'] = (isset($cols[$k]['span']) && is_array($cols[$k]['span']) && !empty($cols[$k]['span']))
			? ($cols[$k]['root']['branch_depth']+$cols[$k]['weight'])
			: (1);
}

function html_table__flat_cols( &$cols, &$parent = null, &$root = null )
{
	$is_root = is_null( $root );
	if( !isset( $branches ) || is_null( $branches ) )
		if( is_null( $parent ) ) $branches =& $cols;
		else $branches =& $parent['span'];
	
	foreach( array_keys( $branches ) as $k )
	{
		if( !is_null( $parent ) )
			$branches[$k]['parent'] =& $parent;
		
		if( $is_root )
			$root =& $branches[$k];
		else
			$cols[] =& $branches[$k];
		
		$branches[$k]['id'] = ($is_root)?$k:(sizeof($cols)-1);
		$branches[$k]['root'] =& $root;
		
		if( isset( $branches[$k]['span'] ) && is_array( $branches[$k]['span'] ) && !empty( $branches[$k]['span'] ) )
			html_table__flat_cols( $cols, $branches[$k], $root );
	}
}

function html_table__build_header( $cols )
{
	$_r = array( );
	foreach( array_keys( $cols ) as $k )
	{
		$_r[ $cols[$k]['depth'] ][ $cols[$k]['id'] ] =& $cols[$k];
	}
	
	//ini_set( 'xdebug.var_display_max_depth', '10' );var_dump( $_r );var_dump( $cols );die;
	
	return $_r;
}


function html_spoiler( $title, $data )
{
	return html_spoiler_start( $title ) . $data . html_spoiler_end( );
}

function html_open_spoiler( $title )
{
	$id = 'spoiler'.rand(10000,99999);
	$r = html_open_element( 'div', array( 'class' => 'spoiler-container', 'onclick' => '$(this).find(".spoiler-data").toggle(500);', 'id' => $id ) );
	$r .= html_element( 'h4', array( 'text' => $title ) );
	$r .= html_open_element( 'div', array( 'class' => 'spoiler-data' ) );
	$r .= html_element( 'script', array( 'html' => 'addLoadEvent(function(){ $("#'.$id.' .spoiler-data").hide(); });' ) );
	return $r;
}

function html_close_spoiler( )
{
	$r = html_close_element( 'div' );
	$r .= html_close_element( 'div' );
	return $r;
}

